面向对象三大特性
一、简介
1、封装
1
| 封装就是隐藏对象的属性和实现细节,仅对外公开接口(公有的方法),控制在程序中的属性的读和修改的访问级别,将数据和行为进行有机的结合,形成一个整体。这个整体就是“类”,其中数据和方法都是类中的成员。
|
2、继承
1
| 继承:子类直接使用父类定义好的属性和方法,从而实现代码的复用。code reuse
|
3、多态
1
| 向不同的对象发送同一条信息,不同的对象在接受到信息后,会做出不同的反应.
|
二、封装
- 将属性和方法进行有机结合,形成一个整体
- 将不想暴露给外界的成员私有化,外界只能通过公有的接口访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Person: def __init__(self, name, age): self.__name = name self.__age = age
def get_age(self): return self.__age
def set_age(self,age): if age < 0 or age > 150: print("年龄不合法") else: self.__age = age
def __str__(self): return f"姓名:{self.__name},年龄:{self.__age}"
p = Person('Tom',18) print(p)
p.set_age(200) print(p)
|
1 2 3 4 5 6
| age = property(get_age, set_age, delete_age)
p.age = 20 print(p.age)
del p.age
|
1 2 3 4 5
| print(p.__dict__) del p.age print(p.__dict__)
print(Person.__dict__)
|
三、继承
1 2 3 4 5 6 7 8 9 10
| 1. 子类直接使用父类定义好的属性和方法,从而实现代码复用.
2. 在Python中如果程序需要,可以让一个类去继承另一个类,被继承的类称为父类或者超类,也可以称为基类,继承的类称为子类或派生类.
3. 如果创建类时,没有手动去设置父类,默认继承自object类,顶层类 (在Python中,万物皆对象)
4. 继承是is-a的关系 一个学生是一个人 一个毕业生是一个学生
5. 当子类继承父类后,会拥有父类所有的属性和方法
|
1、简单的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person: def __init__(self,name,age): self.name = name self.age = age def __str__(self): return f"姓名:{self.name},年龄:{self.age}"
class Student(Person): pass
s = Student()
s = Student('Tom',18) print(s)
|
2、私有成员
子类会继承父类的私有成员,但是子类并不能直接去访问继承自父类的私有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Person: def __init__(self, name, age): self.__name = name self.__age = age
def __str__(self): return f"姓名:{self.__name},年龄:{self.__age}"
class Student(Person): def output(self): return self.__name
s = Student('Tom',18) s.output(
|
如何解决:
- 子类通过继承自父类的公有的方法来访问继承自父类的私有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Person: def __init__(self, name, age): self.__name = name self.__age = age
def get_name(self): return self.__name
def __str__(self): return f"姓名:{self.__name},年龄:{self.__age}"
class Student(Person): def output(self): print(self.get_name())
s = Student('Tom',18) s.output()
|
- 将父类的私有成员改为受保护的成员 (有问题)
- 声明属性时,使用一个_属性名
- 对于外界依然是“私有”的,可以访问不报错,会有警告
- 子类可以直接去访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Person: def __init__(self, name, age): self._name = name self._age = age
def get_name(self): return self._name
def __str__(self): return f"姓名:{self._name},年龄:{self._age}"
class Student(Person): def output(self): print(self._name)
p = Person('Jack', 20) print(p._name)
s = Student('Tom', 18) s.output()
print(s._name)
|
3、方法重写
子类定义了与父类方法名一致的访问,称为方法的重写(覆盖override)
1 2 3 4 5 6 7 8 9
| class Father: def who_am_i(self): print('我是爸爸') class Son(Father): pass
son = Son() son.who_am_i()
|
所以,如果子类有需要,需要去重写父类的同名方法:
1 2 3 4 5 6 7 8 9 10 11 12
| class Father: def who_am_i(self): print('我是爸爸')
class Son(Father): def who_am_i(self): print('我是儿子')
son = Son() son.who_am_i()
|
4、子类的__init__
方法
当子类没有重写父类的__init__
,在实例化子类对象时会默认调用父类的__init__
方法
如果子类重写了__init__
方法,此时再实例化子类对象时,就不再隐式地去调用父类的__init__
方法了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person: def __init__(self, name, age): self.name = name self.age = age
def __str__(self): return f"姓名:{self.name},年龄:{self.age}"
class Student(Person): def __init__(self,id): self.id = id
s = Student(1) print(s)
|
此时再去访问name时,会提示没有name属性,原因于父类的init未被调用。所以如果子类重写了init方法,为了能够使用或扩展父类的行为,最好显式地调用父类的init方法
1 2 3 4 5 6 7 8 9 10 11
| class Student(Person): def __init__(self,id,name,age): self.id = id Person.__init__(self,name,age)
s = Student(1,'Tom',18) print(s.__dict__) print(s)
|
1 2 3 4 5
| class Student(Person): def __init__(self,id,name,age): self.id = id super().__init__(name,age)
|
5、多继承
Python是支持多继承,一个子类可以有多个父类
1 2 3 4 5 6 7 8 9
| class A: a = 100 class B: b = 200 class C(A,B): c = 300 obj = C() print(obj.a)
|
6、菱形继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class A: def __init__(self): print('A init')
class B(A): def __init__(self): print('B init') A.__init__(self)
class C(A): def __init__(self): print('C init') A.__init__(self)
class D(B,C): def __init__(self): print('D init') B.__init__(self) C.__init__(self)
d = D()
|
结果:
1 2 3 4 5 6
| D init B init A init C init A init # 从结果得知,A的init被调用了两次,造成资源的浪费
|
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class A: def __init__(self): print('A init')
class B(A): def __init__(self): print('B init') super().__init__()
class C(A): def __init__(self): print('C init') super().__init__()
class D(B,C): def __init__(self): print('D init') super().__init__()
d = D()
|
在Python中,已经定义好了一个魔法方法来帮助我们去查看类的继承顺序,super()按照MRO的顺序来进行继承。
1 2 3 4 5
| # 补充super(): - super(type,obj) 和 super() 等价 - type:类型 默认是当前类 也可以是MRO链式结构中的父类 - obj :对象 默认是当前对象 通过super()底层是使用MRO找到当前类的继承链,从而通过继承链来调用其父类,避免顶层类被多次调用的问题
|
四、多态
1 2
| 1. 多态:多种形态 2. 多态性:向不同的对象发送相同的指令,不同的对象会做出不同的响应
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Animal: def speak(self): pass class Dog(Animal): def speak(self): print('汪汪汪') class Cat(Animal): def speak(self): print('喵喵喵') d = Dog() c = Cat() d.speak() c.speak()
def animal_speak(animal): animal.speak() animal_speak(d) animal_speak(c)
|
1 2 3
| 多态性好处: 1. 增加程序的灵活性,以不变应万变,不论对象千变万化,使用者都是同一种形式去调用 animal.speak(ojb),但是不同的对象做出的响应是不一样的 2. 增加了程序的可扩展性
|
五、内置函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| # 1. issubclass(cls,class) 判断一个类是否是另一个类的子类 class A: pass class B(A): pass print(issubclass(A,B)) # False print(issubclass(B,A)) # True
# 2. isinstance(obj,class) 判断某个对象是否为类/子类的实例 a = A() print(isinstance(a,A)) # True print(isinstance(B(),A)) # True print(isinstance(B(),object)) # True
# 3. hasattr() 判断对象是否有某个属性 class A: def __init__(self): self.a = 100 self.b = 200
obj = A() print(hasattr(obj,'a')) # True
# 4. getattr(obj,name[,default]) # 获取对象的属性 如果不存在会报错 如果设置了默认值,不存在时返回默认值 print(getattr(obj,'a')) # 100 和 obj.a是等价的 print(getattr(obj,'c')) # 报错 print(getattr(obj,'c',300)) # 返回300
# 5. setattr(obj,name,value) # 设置对象属性值,不存在时添加一个新的 setattr(obj,'c',500) print(obj.c)
# 6. delattr(obj,name) # 删除对象的某个属性 delattr(obj,'a') print(obj.__dict__)
|